use cgmath::{num_traits::Signed, vec2, BaseNum, Vector2};

#[derive(Debug, Clone, Copy)]
pub struct Rect2D<S> {
    pub left: S,
    pub right: S,
    pub bottom: S,
    pub top: S,
}

impl<S: Copy> Rect2D<S> {
    pub fn new(left: S, right: S, bottom: S, top: S) -> Self {
        Self {
            left,
            right,
            bottom,
            top,
        }
    }

    pub fn left_top(&self) -> Vector2<S> {
        vec2(self.left, self.top)
    }

    pub fn left_bottom(&self) -> Vector2<S> {
        vec2(self.left, self.bottom)
    }

    pub fn right_top(&self) -> Vector2<S> {
        vec2(self.right, self.top)
    }

    pub fn right_bottom(&self) -> Vector2<S> {
        vec2(self.right, self.bottom)
    }
}

impl<S: BaseNum> Rect2D<S> {
    pub fn from_origin(width: S, height: S) -> Self {
        Self {
            left: S::zero(),
            right: width,
            bottom: S::zero(),
            top: height,
        }
    }

    pub fn from_position_size(position: Vector2<S>, size: Vector2<S>, pivot: Vector2<S>) -> Self {
        let left = position.x - size.x * pivot.x;
        let right = position.x + size.x * (S::one() - pivot.x);
        let bottom = position.y - size.y * pivot.y;
        let top = position.y + size.y * (S::one() - pivot.y);

        Self {
            left,
            right,
            bottom,
            top,
        }
    }

    pub fn offset(&self, offset: Vector2<S>) -> Rect2D<S> {
        Rect2D {
            left: self.left + offset.x,
            right: self.right + offset.x,
            bottom: self.bottom + offset.y,
            top: self.top + offset.y,
        }
    }

    pub fn contains(&self, p: Vector2<S>) -> bool {
        (self.left..self.right).contains(&p.x) && (self.bottom..self.top).contains(&p.y)
    }
}

impl<S: BaseNum + Signed> Rect2D<S> {
    pub fn width(&self) -> S {
        (self.right - self.left).abs()
    }

    pub fn height(&self) -> S {
        (self.top - self.bottom).abs()
    }

    pub fn position(&self, pivot: Vector2<S>) -> Vector2<S> {
        vec2(
            self.left + self.width() * pivot.x,
            self.bottom + self.height() * pivot.y,
        )
    }
}
